﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EPiServer.Core;
using System.Reflection;
using EPiServer.Models;
using EPiServer.Core.Html;
using System.Globalization;

namespace EPiServer
{
    public static class PageDataExtender
    {
        //TODO: tänk om initieringen av start...
        [ThreadStatic]
        private static StartPage Start;

        public static void PopulateList<T>(this PageReference rootPage, List<T> model, int count)
            where T : IEPiServerPageModel, new()
        {
            if (PageReference.IsNullOrEmpty(rootPage))
                return;

            var pages = EPiServer.DataFactory.Instance.GetChildren(rootPage);
            pages.PopulateList(model, count);
        }

        public static void PopulateList<T>(this PageDataCollection pageList, IList<T> model)
            where T : IEPiServerPageModel, new()
        {
            pageList.PopulateList(model, 0);
        }

        public static void PopulateList<T>(this PageDataCollection pageList, IList<T> model, int count)
            where T : IEPiServerPageModel, new()
        {
            if(pageList == null || model == null)
                return;

            foreach (var child in pageList)
            {
                var item = new T();
                child.PopulateModel(item);
                model.Add(item);

                if (--count == 0)
                    return;
            }        
        }

        internal static void PopulateList(this PageDataCollection pageList, IList model, int count, Type modelType)
        {
            if (pageList == null || model == null)
                return;

            foreach (var child in pageList)
            {
                var item = (IEPiServerPageModel)Activator.CreateInstance(modelType);
                child.PopulateModel(item, modelType);
                model.Add(item);
                if (--count == 0)
                    return;
            }
        }

        public static void PopulateModel<T>(this PageData pageData, T model)
            where T : IEPiServerPageModel
        {
            PopulateModel(pageData, model, null);    
        }

        private static void PopulateModel<T>(this PageData pageData, T model, Type modelType)
            where T : IEPiServerPageModel
        {
            if (pageData == null || model == null)
                return;

            Type propertyType = modelType ?? typeof(T);

            var propertyList = propertyType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var property in propertyList.Where(p => p.CanWrite))
            {
                var propData = pageData.Property[property.Name];
                if (propData != null)
                {
					if (propData.PropertyValueType != property.PropertyType && !(propData.PropertyValueType == typeof(int) && property.PropertyType.IsEnum))
                        throw new InvalidCastException(String.Format("Property '{0}' is of type '{1}'. Expected was {2}", property.Name, propData.PropertyValueType, property.PropertyType));

                    if (propData.Value != null)
                    {
                        try
                        {
                            property.SetValue(model, propData.Value, null);
                        }
                        catch (Exception e)
                        {
                            throw new InvalidCastException("Failed to set property '" + property.Name + "'", e);
                        }
                    }
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Missing property: " + property.Name);
                }
            }

            //TODO: plocka bort beroende till typ.. 
            if (model is BaseModel)
            {
                var baseModel = model as BaseModel;
                PopulateEPiServerMetaData(pageData, baseModel);
                 
                if(Start != null)
                {
                    baseModel.Start = Start;
                }
                else
                {
                    if (baseModel.PageLink.ID == PageReference.StartPage.ID)
                    {
                        Start = baseModel as StartPage;
                        baseModel.Start = Start; //TODO: smäller om man populerar startsidan som en annan model...
                    }
                    else
                    {
                        Start = new StartPage();
                        baseModel.Start = Start;
                        var startPageData = DataFactory.Instance.GetPage(PageReference.StartPage);
                        startPageData.PopulateModel(baseModel.Start);
                    }
                }

                PopulateMenus(baseModel);
                PopulateMetadata(baseModel);
                PopulateLanguages(baseModel);

            }

            model.LinkURL = pageData.LinkURL;
            model.PageLink = pageData.PageLink;



            foreach (var property in propertyList.Where(p => p.CanWrite))
            {
                if (typeof(IList).IsAssignableFrom(property.PropertyType) && property.PropertyType.IsGenericType)
                {
                    var attributes = property.GetCustomAttributes(typeof(PopulateFromAttribute),true);
                    if (attributes != null && attributes.Length > 0)
                    {
                        Type genericType = property.PropertyType.GetGenericArguments()[0];
                        var ca = attributes[0] as PopulateFromAttribute;
                        var list = (IList)property.GetValue(model, null);
                        if (list == null)
                        {
                            list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(genericType));
                            property.SetValue(model, list, null);
                        }

                        int count = 0;
                        if (ca.CountProperty != null)
                        {
                            count = (int)propertyType.GetProperty(ca.CountProperty).GetValue(model, null);
                        }

                        var pageRef =
                            (PageReference)propertyType.GetProperty(ca.PageReferenceProperty).GetValue(model, null);
                        if (!PageReference.IsNullOrEmpty(pageRef))
                        {
                            PopulateList(DataFactory.Instance.GetChildren(pageRef), list, count, genericType);
                        }
                        //else log
                    }
                }
            }

        }

        private static void PopulateEPiServerMetaData(PageData pageData, IEPiServerPageModel model)
        {
            model.Changed = pageData.Changed;
            model.Created = pageData.Created;
            model.LanguageBranch = pageData.LanguageBranch;
            model.LinkURL = pageData.LinkURL;
            model.PageLink = pageData.PageLink;
        }

		private static void FilterForMenu(PageDataCollection pages)
		{
			Filters.FilterForVisitor.Filter(pages);
			for (int i = pages.Count - 1; i >= 0; i--)
			{
				if (!pages[i].VisibleInMenu) 
				{
					pages.RemoveAt(i);
				}
			}

		}

    	//TODO: Slå ihop bägge menypopuleringarna. loopa från den man står o skapa ett lista med alla öppna upp till root. flagga dom som är öppna
        private static void PopulateMenus(Models.BaseModel model)
        {
            List<int> openPages = new List<int>();
            PageReference currentRef = model.PageLink;
            while (!PageReference.IsNullOrEmpty(currentRef) && currentRef != PageReference.RootPage)
            {
                openPages.Add(currentRef.ID);
                var d = DataFactory.Instance.GetPage(currentRef);
                currentRef = d.ParentLink;
            }

            //TODO: kolla mainmenucontainer
            var pages = DataFactory.Instance.GetChildren(model.Start.PageLink);
        	FilterForMenu(pages);
            pages.PopulateList(model.Menu);
            var activeMenuItem = model.Menu.Where(p => openPages.Contains(p.PageLink.ID)).FirstOrDefault();
            if (activeMenuItem != null)
            {
                PopulateSubItems(model.SubMenu, activeMenuItem, openPages);
            }
        }

        private static void PopulateSubItems(List<MenuLink> menu, MenuLink active, List<int> openPages)
        {
	        active.Expanded = true;

	        var pages = DataFactory.Instance.GetChildren(active.PageLink);
			FilterForMenu(pages);
	        pages.PopulateList(menu);
	        active = menu.Where(p => openPages.Contains(p.PageLink.ID)).FirstOrDefault();

	        if(active != null)
	        {    
	            PopulateSubItems(active.SubItems, active, openPages); 
	        }
        }

        private static void PopulateLanguages(Models.BaseModel model)
        {
            //låt bli att populera om man inte använder språk.. ignorerar det för stunden så man kan testa
            //sen är det väll tveksamt om den här ska ta hänsyn till vad gui:t visar o inte..
            //if (!Configuration.Settings.Instance.UIShowGlobalizationUserInterface)
            //    return;

            PageDataCollection languageBranches = DataFactory.Instance.GetLanguageBranches(model.PageLink);
            if (languageBranches == null || languageBranches.Count == 0) { return; }

            foreach (PageData languageBranch in languageBranches)
            {
                if (languageBranch.LanguageBranch != model.LanguageBranch&& EPiServer.DataAbstraction.LanguageBranch.Load(languageBranch.LanguageID).Enabled)
                {
                    //TODO: lite trixigt sätt att ändra url..
                    var builder = new UrlBuilder(model.LinkURL);
                    builder.QueryCollection["epslanguage"] = languageBranch.LanguageID;
                    model.Languages.Add(new Link
                    {
                        PageName = new CultureInfo(languageBranch.LanguageID).NativeName,
                        LinkURL = builder.ToString(),
                        PageLink = languageBranch.PageLink
                    });
                }
            }

        }

        private static void PopulateMetadata(BaseModel model)
        {
            string metaDescription = model.MetaDescription;
            if (String.IsNullOrEmpty(metaDescription))
            {
                metaDescription = model.MainIntro;
                if (!String.IsNullOrEmpty(metaDescription))
                {
                    metaDescription = TextIndexer.StripHtml(metaDescription, 0);
                    if (metaDescription.Length > 255)
                        metaDescription = metaDescription.Substring(0, 252) + "...";
                }
            }
            if (!String.IsNullOrEmpty(metaDescription))
            {
                model.MetaList.Add(new MetaTag
                {
                    Name = "description",
                    Content = metaDescription
                });
            }

            // Keywords
            model.MetaList.Add(new MetaTag
            {
                Name = "keywords",
                Content = model.MetaKeywords
            });

            // Author
            model.MetaList.Add(new MetaTag
            {
                Name = "author",
                Content = model.MetaAuthor
            });

            // Rating
            model.MetaList.Add(new MetaTag
            {
                Name = "rating",
                Content = "General"
            });

            // Revisit each month
            model.MetaList.Add(new MetaTag
            {
                Name = "revisit-after",
                Content = "4 weeks"
            });

            // Generator
            model.MetaList.Add(new MetaTag
            {
                Name = "generator",
                Content = "EPiServer-MVC"
            });

            // Robots
            model.MetaList.Add(new MetaTag
            {
                Name = "robots",
                Content = "all"
            });

            // Charset
            model.MetaList.Add(new MetaTag
            {
                Name = "Content-Type",
                Content = "text/html; charset=UTF-8",
                HttpEquiv = true
            });

            // Created - GMT format
            if (model.Created != DateTime.MinValue)
            {
                model.MetaList.Add(new MetaTag
                {
                    Name = "creation_date",
                    Content = model.Created.ToString("R")
                });
            }
            // Last modified data, in GMT format - Note, same as revised
            if (model.Changed != DateTime.MinValue)
            {
                model.MetaList.Add(new MetaTag
                {
                    Name = "last-modified",
                    Content = model.Changed.ToString("R")
                });
            }
            // Revised - GMT format
            if (model.Changed != DateTime.MinValue)
            {
                model.MetaList.Add(new MetaTag
                {
                    Name = "revised",
                    Content = model.Changed.ToString("R")
                });
            }
            model.MetaList.Add(new MetaTag
            {
                Name = "Content-Language",
                Content = model.LanguageBranch,
                HttpEquiv = true
            });
        }

    }
}